/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using System.Collections;
using System.Data;
using EXtremecode.DataAccessLayer;
using EXtremecode.Utils;
using EXtremecode.Common;
using DAConfig=EXtremecode.DataAccessLayer.Configurations; 
using System.Collections.Generic;


namespace EXtremecode.BusinessLayer
{
    
    
    
    public abstract class BusinessObjectCollection : IEnumerator, IEnumerable
	{
        protected Hashtable m_filterCriteriaFields = new Hashtable();
        protected string m_objectTypeKey = string.Empty;
        
        protected long? m_totalRecords;
        protected bool m_isSortingTypeAscending = true;
        protected string m_sortedBy = string.Empty;
        protected string m_whereClause = string.Empty;
        protected string m_queryJoins = string.Empty;
        protected int m_pageSize = 10; //default
        protected int m_pageNumber = 0;
        protected bool m_returnRowCount = false;

        //command pools
        private static Dictionary<string, ObjectPool> m_daSelectCommandPools = new Dictionary<string, ObjectPool>();
        private static Dictionary<string, ObjectPool> m_daDeleteCommandPools = new Dictionary<string, ObjectPool>();

        //Adapter pools
        private static Dictionary<string, ObjectPool> m_adapterPools = new Dictionary<string, ObjectPool>();

		
        protected DataAccessor m_da;
		private	DADataReader m_rdr;


        private bool autoRefresh = true;
        private bool gettingFromCache = false;
        
        //for temporary use only
        private bool m_isDataFound = false;
		protected bool enumeratorReturningPapers=false;
        private bool enumeratorDecisionFlag = false;
		private bool m_isTransactionBased=false;
		
		
		//command handler
        protected CommandCreatorHandler selectCmdCreator,updateCmdCreator,deleteCmdCreator,insertCmdCreator; 

		protected BusinessObjectCollection():this(DataAccessorCollection.DefaultAccessorName){}
		protected BusinessObjectCollection(string strConnectionId)
		{
			try
			{
				PopulateWithCommandCreators(ref selectCmdCreator,ref updateCmdCreator,ref insertCmdCreator,ref deleteCmdCreator);
				m_da=DataAccessorCollection.Instance[strConnectionId];
                m_objectTypeKey = string.Format(BusinessObject.OBJECT_TYPE_KEY_FORMAT_STRING, strConnectionId, Table_Name);

			}
			catch(Exception ex)
			{
				throw Logging.Logger.Write(ex);
			}
			finally
			{
				//Logging.Logger.Write();
			}

		}



        private DACommand GetDeleteCommand()
        {

            //create pool if not created.
            lock (m_daDeleteCommandPools)
            {
                if (!m_daDeleteCommandPools.ContainsKey(m_objectTypeKey))
                {
                    ObjectPool pool =
                        new ObjectPool(m_objectTypeKey + " (Multi Delete Command)", (CreateObjectDelegate<IPooledObject>)delegate
                        {
                            return m_da.Create_DeleteDACommand(Table_Name, GetFieldInfoCollection(), deleteCmdCreator);
                        });

                    m_daDeleteCommandPools.Add(m_objectTypeKey, pool);

                }
            }
            return (DACommand)m_daDeleteCommandPools[m_objectTypeKey].GetObject();

        }

        private DACommand GetSelectCommand()
        {

            //create pool if not created.
            lock (m_daSelectCommandPools)
            {
                if (!m_daSelectCommandPools.ContainsKey(m_objectTypeKey))
                {
                    ObjectPool pool =
                        new ObjectPool(m_objectTypeKey + " (Select Command)", (CreateObjectDelegate<IPooledObject>)delegate
                        {
                            return m_da.Create_SelectDACommand(View_Name, GetFieldInfoCollection(), selectCmdCreator);
                        });

                    m_daSelectCommandPools.Add(m_objectTypeKey, pool);

                }
            }
            return (DACommand)m_daSelectCommandPools[m_objectTypeKey].GetObject();

        }

        private DAAdapter GetAdapter()
        {

            //create pool if not created.
            lock (m_adapterPools)
            {
                if (!m_adapterPools.ContainsKey(m_objectTypeKey))
                {
                    ObjectPool pool =
                        new ObjectPool(m_objectTypeKey + " (Adapter)", (CreateObjectDelegate<IPooledObject>)delegate
                        {
                            return m_da.Create_Adapter(View_Name, Table_Name, GetFieldInfoCollection(), selectCmdCreator, updateCmdCreator, insertCmdCreator, deleteCmdCreator);
                        });

                    m_adapterPools.Add(m_objectTypeKey, pool);

                }
            }
            return (DAAdapter)m_adapterPools[m_objectTypeKey].GetObject();


        }

		public IEnumerator GetEnumerator()
		{
            if (enumeratorDecisionFlag)
            {
                //decision is already taken. just reset this flag.
                enumeratorDecisionFlag = false;
            }
            else
            {
                //not decision has been taken for enumerator
                //so return default enumerator.
                enumeratorReturningPapers = false; //return Bussiness Objects.
                m_isTransactionBased = false; //defaut is not transaction based.
            }
          
            PopulateReader();
			return this;	
		}
        public BusinessObjectCollection GetBusinessObjects(bool isTransactionBased)
        {
            //take decision
            enumeratorDecisionFlag = true;

            m_isTransactionBased = isTransactionBased;
            enumeratorReturningPapers = false;
            
            return this;

        }
        public BusinessObjectCollection GetBusinessObjects()
		{
			return GetBusinessObjects(false);
		}
        public BusinessObjectCollection GetBOPapers(bool isTransactionBased)
        {
            //take decision
            enumeratorDecisionFlag = true;

            m_isTransactionBased = isTransactionBased;
            enumeratorReturningPapers = true;
            
            return this;

        }
        public BusinessObjectCollection GetBOPapers()
		{
			return GetBOPapers(false);		
		}

		
        private void PopulateReader()
		{
			try
			{
				m_isDataFound = false;

                //get command, execute reader, and release commad to pool.
                DACommand daCommand = GetSelectCommand();
                try
                {
                    CriteriaToCommand(daCommand);
                    try
                    {

                        if (gettingFromCache)
                        {
                            m_rdr = daCommand.GetDataReaderFromCache();
                        }
                        else if (m_isTransactionBased)
                        {
                            m_rdr = daCommand.ExecuteReaderForTransaction();
                        }
                        else
                        {
                            m_rdr = daCommand.ExecuteReader();
                        }
                    }
                    catch
                    {
                        if (m_rdr != null)
                        {
                            m_rdr.Close();
                        }
                        throw;
                    }
                    finally
                    {
                        CleanDirtyParams(daCommand);
                    }

                }
                finally
                {
                    daCommand.Pool.ReleaseObject(daCommand);
                }

				
			}
			catch(Exception ex)
			{
				throw Logging.Logger.Write(ex);
			}
			finally
			{
				Refresh(false);
			}

		}

        private void CriteriaToCommand(DACommand daCommand)
        {
            //commad parameter based filter
            foreach (string key in m_filterCriteriaFields.Keys)
            {
                daCommand[key + "_" + Constants.ParamCustomType.CONDITIONAL] = m_filterCriteriaFields[key];
            }

            //extra filters
            daCommand.WhereClause = m_whereClause;
            daCommand.Joins = m_queryJoins;

            //paging params
            daCommand.TotalRecords = m_totalRecords;
            daCommand.IsSortingTypeAscending = m_isSortingTypeAscending;
            daCommand.SortedBy = m_sortedBy;
            daCommand.PageSize = m_pageSize;
            daCommand.PageNumber = m_pageNumber;

            //behavior
            daCommand.ReturnRowCount = m_returnRowCount;

       }

        private void CleanDirtyParams(DACommand daCommand)
        {
            foreach (string key in m_filterCriteriaFields.Keys)
            {
                daCommand.CleanParam(key + "_" + Constants.ParamCustomType.CONDITIONAL);
            }

            
        }


		public void Reset()
		{
		}
		public bool MoveNext()
		{
			if(m_rdr == null) return false;  
			m_isDataFound = m_rdr.MoveNext();
			if(!m_isDataFound)
			{
				m_rdr.Close();
			}
			return m_isDataFound;
		}
		public void Close()
		{
            if (m_rdr != null)
            {
                m_rdr.Close();
            }
		}
		public object Current
		{
			get
			{
				try
				{
                    if (m_isDataFound)
                    {
                        BusinessObjectPaper paper = (BusinessObjectPaper)CreateBOPaper(GetFieldInfoCollection());
                        BusinessObject.ReaderToPaper(paper, m_rdr);

                        if (enumeratorReturningPapers)
                        {
                            return paper;
                        }
                        else
                        {
                            return CreateBusinessObject(paper);
                        }
                    }
                    else
                    {
                        return null;
                    }
				}
				catch(Exception ex)
				{
                    if (m_rdr != null)
                    {
                        m_rdr.Close();
                    }
                    throw Logging.Logger.Write(ex);
				}
				finally
				{
					//Logging.Logger.Write();
				}
			
			}
        }

        #region Value Added Methods and Properties
        public string GetFieldValue(string fieldName)
		{
			string val=string.Empty;
			BusinessObjectPaper paper=GetBOPaper();
			if(paper != null && paper[fieldName] != null)
			{
				val = paper[fieldName].ToString();
			}
			return val;
		}
		public string GetFieldValues(string fieldName)
		{
			return GetFieldValues(fieldName,string.Empty);
		}
		public string GetFieldValues(string fieldName,string valuesEnclosedWith)
		{
			string vals = string.Empty;
			string qoma = string.Empty;
			try
			{
				foreach(BusinessObjectPaper paper in GetBOPapers())
				{
					if(paper != null && paper[fieldName] != null)
					{
						vals += qoma + valuesEnclosedWith + paper[fieldName].ToString() + valuesEnclosedWith;
						qoma = " , ";
					}
				}
			}
			finally
			{
				Close();
			}
			return vals;
        }
        #endregion Value Added Methods and Properties

        public BusinessObject GetBusinessObject()
		{
			return GetBusinessObject(false);
		}
		public BusinessObject GetBusinessObject(bool isTransactionBased)
		{
            enumeratorReturningPapers = false;
            return GetObject(isTransactionBased) as BusinessObject;
		}
        private object GetObject(bool isTransactionBased)
        {
            m_isTransactionBased = isTransactionBased;
            PopulateReader();
            try
            {
                object obj = null;
                if (MoveNext())
                    obj = Current;
                return obj;
            }
            finally
            {
                if (m_rdr != null)
                {
                    m_rdr.Close();
                }
            }
        }

		public BusinessObjectPaper GetBOPaper()
		{
			return GetBOPaper(false);
		}
		public BusinessObjectPaper GetBOPaper(bool isTransactionBased)
		{
            enumeratorReturningPapers = true;
            return GetObject(isTransactionBased) as BusinessObjectPaper;
		}


        public int DeleteRecords(bool isTransationBased)
		{
            
            try
            {
                //get command, execute, and release commad to pool.
                DACommand daCommand = GetDeleteCommand();
                int recordsEffected = 0;
                try
                {
                    CriteriaToCommand(daCommand);
                    try
                    {

                        if (isTransationBased)
                        {
                            recordsEffected = daCommand.ExecuteNoneQueryForTransaction();
                        }
                        else
                        {
                            recordsEffected = daCommand.ExecuteNoneQuery();
                        }
                    }
                    finally
                    {
                        CleanDirtyParams(daCommand);
                    }

                }
                finally
                {
                    daCommand.Pool.ReleaseObject(daCommand);

                }
                return recordsEffected;
            }
            finally
            {
                Refresh(false);
            }
            
		}
		public void DeleteRecords()
		{
            DeleteRecords(false);
		}
		
		private void Refresh(bool explicitCall)
		{
			
			//*****these need to be reset explicitly.thats why
			/*QueryJoins = string.Empty;
			WhereClause=string.Empty;
			gettingFromCache= false;
			SortedBy=string.Empty;
            IsSortingTypeAscending=true*/


			//*****these line would be excecuted if this method is called expliccitly or
			//*****auto refresh flag is true.
			if(explicitCall || autoRefresh)
			{
                m_filterCriteriaFields.Clear();
				
                //*****if PageNumber is greater than zero, means object is using paging and need to be reset.
				if(PageNumber > 1)
				{
					PageNumber=1;
				}

                
                //refresh reocord count.
                m_totalRecords = null;

			}


            //behavior
            m_returnRowCount = false;            

			

		}

		public void Refresh()
		{
			//call explicitly
			Refresh(true);
		}

		internal DataAccessor DataAccessor
		{
			get
			{
				return m_da;
			}
		}
		internal DADataReader Reader
		{
			get
			{
				return m_rdr;
			}

		}
		public bool HasRows()
		{
			PopulateReader();
			try
			{
				bool rowFound=false;
				if(m_rdr == null)return rowFound;
				rowFound=m_rdr.MoveNext();
				return rowFound;
			}
			finally
			{
                if (m_rdr != null)
                {
                    m_rdr.Close();
                }
			}


		}
		public DataTable GetDataTable()
		{
			DataTable dt=CreateDataTable();
			return dt;
		}

		public DataTable GetCachedTable()
		{
			DataTable dt=CacheRepository.Instance.CachedDataSet[DataTable_Name];
			return dt;
		}

		public void FillDataTable(DataTable dt)
		{
			FillDataTable(dt,false);
		}
		public void FillDataTable(DataTable dt,bool isTransactionBased)
		{
			if(!DataEncryptor.IsAllowed)
			{
				Logging.Logger.Write("Unregistered");
				return;
			}

            try
            {
                //get adapter, fill, and release adapter to pool.
                DAAdapter daAdapter = GetAdapter();
                try
                {
                    CriteriaToCommand(daAdapter.SelectCommand);
                    try
                    {

                        if (gettingFromCache)
                        {
                            daAdapter.FillFromCache(dt);
                        }
                        if (m_isTransactionBased)
                        {
                            daAdapter.FillForTransaction(dt);
                        }
                        else
                        {
                            daAdapter.Fill(dt);
                        }
                    }
                    finally
                    {
                        CleanDirtyParams(daAdapter.SelectCommand);
                    }

                }
                finally
                {
                    daAdapter.Pool.ReleaseObject(daAdapter);

                }
            }
            finally
            {
                Refresh(false);
            }

			
		}
		public DataTable FillDataTable(DataSet ds,bool isTransactionBased)
		{
			FillDataTable(ds.Tables[DataTable_Name],isTransactionBased);
			return ds.Tables[DataTable_Name];
		}
		public DataTable FillDataTable(DataSet ds)
		{
			return FillDataTable(ds,false);
		}
		public DataTable FillDataTable(bool isTransactionBased)
		{
			DataTable dt=GetDataTable();
			FillDataTable(dt,isTransactionBased);
			return dt;
		}
		public DataTable FillDataTable()
		{
			return FillDataTable(false);
		}
		public void UpdateDataTable(DataTable dt)
		{
			UpdateDataTable(dt,false,false);
		}
		public void UpdateDataTable(bool bIgnoreConcurrEx,DataTable dt)
		{
			UpdateDataTable(dt,false,bIgnoreConcurrEx);
		}
		public void UpdateDataTable(DataTable dt,bool isTransationBased)
		{
			UpdateDataTable(dt,isTransationBased,false);
		}
        public void UpdateDataTable(DataTable dt, bool isTransationBased, bool bIgnoreConcurrEx)
        {
            //get adapter, update, and release adapter to pool.
            DAAdapter daAdapter = GetAdapter();
            try
            {
                if (isTransationBased)
                {
                    daAdapter.UpdateForTransaction(dt, bIgnoreConcurrEx);
                }
                else
                {
                    daAdapter.Update(dt, bIgnoreConcurrEx);
                }

            }
            finally
            {
                daAdapter.Pool.ReleaseObject(daAdapter);
            }
            
            //refresh cache item
            RefreshCache();
        }

        public long GetRecordCount(bool isTransactionBased)
        {
            if (!DataEncryptor.IsAllowed)
            {
                Logging.Logger.Write("Unregistered");
                return 0;
            }


            long recordCount = 0;
            m_isTransactionBased = isTransactionBased;

            try
            {
                m_returnRowCount = true;
                PopulateReader();

                if (gettingFromCache)
                {
                    recordCount = (long)m_rdr.ClassicDataReader.RecordsAffected;
                }
                else
                {
                    try
                    {
                        if (m_rdr.MoveNext())
                        {
                            recordCount = Convert.ToInt64(m_rdr[0]);
                        }
                        else
                        {
                            recordCount = 0;
                        }
                    }
                    finally
                    {
                        if (m_rdr != null)
                        {
                            m_rdr.Close();
                        }
                    }
                }
            }
            finally
            {
                m_totalRecords = recordCount;
                Refresh(false);
            }

            return recordCount;
        }
		public long GetRecordCount()
		{
			return GetRecordCount(false);
		}
		
		
		public virtual object this[string fieldName]
		{
			get
			{
				return m_filterCriteriaFields[fieldName];
			}
			
			set
			{
                if (value != null && value.GetType() == typeof(DateTime)
                  && fieldName.EndsWith(Constants.ColumnSuffix.TO))
                {
                    DateTime dt = Convert.ToDateTime(value);
                    if (dt.Hour == 0 && dt.Minute == 0 && dt.Second == 0)
                    {
                        value = dt.AddDays(1).AddSeconds(-1);
                    }

                }
                
                m_filterCriteriaFields[fieldName] = value;
			}
		}

		public string QueryJoins
		{
			get
			{
                return m_queryJoins;
			}
			set
			{
                m_queryJoins = value;
			}
		}
		public string WhereClause
		{
			get
			{
				return m_whereClause;
			}
			set
			{
				m_whereClause=value;
			}
		}

		public int PageSize
		{
			get
			{
				return m_pageSize;
			}
			set
			{
				m_pageSize = value;
			}
		}

        public bool UsePaging
        {
            get
            {
                return (PageNumber > 0);
            }
            set
            {
                if (value)
                {
                    PageNumber = 1;
                }
                else
                {
                    PageNumber = 0;
                }

            }

        }
	
		public int PageNumber
		{
			get
			{
				return m_pageNumber;
			}
			set
			{
                m_pageNumber = value;
			}
		}

		public bool GettingFromCache
		{
			get
			{
				return gettingFromCache;
			}
			set
			{
				gettingFromCache=value;
			}
		}

		public string SortedBy
		{
			get
			{
				return m_sortedBy;
			}
			set
			{
				m_sortedBy=value;
			}
		}
		public bool IsSortingTypeAscending
		{
			get
			{
				return m_isSortingTypeAscending;
			}
			set
			{
                m_isSortingTypeAscending = value;
			}
		}

		public bool AutoRefresh
		{
			get
			{
				return autoRefresh;
			}
			set
			{
				autoRefresh=value; 
			}
		}

		public abstract string View_Name{get;}
	
		public abstract string Table_Name{get;}
		public abstract string DataTable_Name{get;}

		public abstract BusinessObjectRelationCollection ChildRelations
		{get;}
		public abstract BusinessObjectRelationCollection ParentRelations
		{get;}
		protected abstract FieldInfoCollection  GetFieldInfoCollection();
		protected abstract object CreateBusinessObject(BusinessObjectPaper boPaper);
        protected abstract object CreateBOPaper(FieldInfoCollection fields);
		protected abstract DataTable CreateDataTable();
		protected abstract void PopulateWithCommandCreators(ref CommandCreatorHandler selectCmdCreator,ref CommandCreatorHandler updateCmdCreator,ref CommandCreatorHandler insertCmdCreator,ref CommandCreatorHandler deleteCmdCreator);
		protected abstract void RefreshCache();

 

	}
}
